#pragma once
namespace graph
{
   struct graph_t;
   namespace details
   {
      typedef size_t cell_id_1dim_t;
      typedef size_t cell_id_2dim_t;
      typedef std::pair<cell_id_1dim_t, cell_id_2dim_t> cell_id_t;
      
      typedef boost::unordered_multimap<cell_id_t, edge_t> cell_edge_map_t;
      typedef boost::unordered_multimap<cell_id_t, vertex_id_t> cell_vertex_map_t;

      typedef cell_vertex_map_t::const_iterator const_cell_vertex_iterator_t;
      typedef std::pair<const_cell_vertex_iterator_t, const_cell_vertex_iterator_t> cell_vertex_iterator_pair_t;
      
      typedef cell_edge_map_t::const_iterator const_cell_edge_iterator_t;
      typedef std::pair<const_cell_edge_iterator_t, const_cell_edge_iterator_t> cell_edge_iterator_pair_t;
      
      typedef std::pair<cell_id_t, cell_id_t> box_t;
      typedef std::pair<mpq_t, mpq_t> point_hp_t;
      typedef std::pair<point_hp_t, point_hp_t> point_pair_hp_t;      
      
      enum delta
      {
         d1 = 1, d2 = 2, d4 = 3
      };
      
      struct graph_metadata_t
      {
         graph_metadata_t(graph_t* graph, bool use_fast_rasterization);         
         template<delta d>
         static point_pair_t get_cell_rect(cell_id_t id);   
         template<delta d>
         static void get_cell_rect_hp(cell_id_t id, mpq_t lat1, mpq_t lon1, mpq_t lat2, mpq_t lon2);   
         template<delta d>
         static cell_id_t get_cell_id(point_t point);
         template<delta d, bool modular_arithm>
         static cell_id_t get_cell_id(point_t point);
         template<delta d>
         static cell_id_t max_cell_id();
         template<delta d, bool modular_arithm>
         cell_id_t get_cell_id(mpq_t lat, mpq_t lon);
         template<delta d>
         cell_id_t get_cell_id(mpq_t lat, mpq_t lon);
         template<int corner_x, int corner_y>
         static box_t devide_box(box_t);
         cell_vertex_iterator_pair_t cell_vertices(cell_id_t id) const;
         template<delta d>
         cell_edge_iterator_pair_t cell_edges(cell_id_t id) const;
         template<delta d>         
         cell_edge_iterator_pair_t get_cells() const;
         
      private:
         graph_t* graph_;         
         cell_edge_map_t cell_edge_map_d1_;
         cell_edge_map_t cell_edge_map_d2_;
         cell_edge_map_t cell_edge_map_d4_;
         cell_vertex_map_t cell_vertex_map_d1_;

         bool use_fast_rasterization_;
         void init_cell_edge_map();
         void init_cell_vertex_map();

         template<delta d>
         cell_edge_map_t& get_cell_edge_map();
         template<delta d>
         cell_edge_map_t const & get_cell_edge_map() const;
         
         template<delta d>
         void rasterize_edge(edge_t const & edge);
         template<delta d>
         void rasterize_edge_hp(edge_t const & edge);
         //scalar multiplication approach
         template<delta d> 
         void rasterize_edge2(edge_t const & edge);
         size_t err_count_;
      };
      //limit when to switch to high precision if def RASTER_HIGH_PREC
      //or to use as maximum delta between two floats to check if they equal
      static lat_lon_t const RASTER_PREC_LIMIT = 0.00002f;
      typedef boost::shared_ptr<graph_metadata_t> graph_metadata_ptr_t;     

      template<delta d>
      struct grad_per_cell_traits_t
      {
         struct ERROR_NO_GRAD_PER_CELL_CONST_SPECIFIED_FOR_THIS_DELTA;
         ERROR_NO_GRAD_PER_CELL_CONST_SPECIFIED_FOR_THIS_DELTA error;
      };
      //0.001 ~ 100 meters per cell
      //0.005f; //550 meters per cell
      //0.0016666f;//1.0/60.0/10.0; //180 meters or 6 seconds per cell
      template<>
      struct grad_per_cell_traits_t<d1>
      {
         static lat_lon_t const value()
         {
            return 0.001f;
         }
      };
      template<>
      struct grad_per_cell_traits_t<d2>
      {
         static lat_lon_t const value()
         {
            return 0.002f;
         }
      };
      template<>
      struct grad_per_cell_traits_t<d4>
      {
         static lat_lon_t const value()
         {
            return 0.004f;
         }
      };
   }
}
